
local inspect = require "ddcl.inspect"
local tcp_channel = require "ddcl.tcp_channel"
local socket = require "ddcl.socket"
local service = require "ddcl.service"
local sha1 = require "ddcl.sha1"
local base64 = require "ddcl.base64"
local utils = require "ddcl.websocket.utils"
local http_utils = require "ddcl.http.utils"



local function check_req(h)

    return true
end

local function attach_fd(M, fd, addr)
    local close_fn
    local channel = tcp_channel(fd, {
        forward = M.forward,
        close_fn = close_fn
    })

    local h = http_utils.read_request_header(channel, M.max_len)
    local key = h.header["sec-websocket-key"]
    assert(key)
    local accept = base64.encode(sha1.encode(key ..
        "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
    local protocol = ""
    local rsp = string.format("HTTP/1.1 101 Switching Protocols\r\n" ..
                        "Upgrade: websocket\r\n" ..
                        "Connection: Upgrade\r\n" ..
                        "Sec-WebSocket-Accept: %s\r\n" ..
                        "%s\r\n", accept, protocol)
    channel.send(rsp)
    check_req(h)
    local c = {
        fd = fd,
        addr = addr,
        channel = channel,
    }
    close_fn = function()
        c.close__()
    end

    function c.send(data, mask, is_text)
        if c.channel then
            local packet = utils.make_packet(data, mask, is_text)
            c.channel.send(packet)
        end
    end

    function c.close()
        if c.channel then
            c.channel.close()
        end
    end

    function c.close__()
        if M.close_fn then M.close_fn(M, c) end
        c.channel = nil
        c.fd = nil
    end

    service.fork(function()
        while c.channel do
            local ret, data = pcall(utils.read_packet, c.channel)
            if not ret or not data then
                if c.channel then
                    c.channel.close()
                end
                return
            end
            if M.message_fn then
                service.fork(function()
                    M.message_fn(M, c, data)
                end)
            end
        end
    end)
    return c
end

local function do_listen(M)
    local fd = socket.bind(M.host, M.port)
    assert(fd, M.host)
    M.fd = fd
    socket.listen(fd)
    socket.forward(fd, function(msg, nfd, addr)
        local ret, c = pcall(attach_fd, M, nfd, addr)
        if not ret then
            return
        end
        if M.connect_fn then
            M.connect_fn(M, c)
        end
    end)
end

local function create(opt)
    local M = {
        host = opt.host,
        port = opt.port,
        forward = opt.forward,
        max_header_len = opt.max_header_len or 2048,
        close_fn = opt.close_fn,
        message_fn = opt.message_fn,
        connect_fn = opt.connect_fn,
    }

    function M.listen()
        do_listen(M)
    end

    function M.close()
        if M.fd then
            socket.close(M.fd)
            M.fd = nil
        end
    end

    return M
end


return create
